package com.example.handler;

import com.example.channel.BootNettyChannel;
import com.example.channel.BootNettyChannelCache;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.InetSocketAddress;

/**
 * @ClassName: BootNettyChannelInboundHandlerAdapter
 * @Author : 13736
 * @Date :2023/7/8  10:13
 * @Description: TODO
 * @Version :1.0
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class BootNettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter {

    /**
     *  注册时执行
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        log.info("--channelRegistered--"+ctx.channel().id().toString());
    }

    /**
     *  离线时执行
     */
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
        log.info("--channelUnregistered--"+ctx.channel().id().toString());
    }

    /**
     * 从客户端收到新的数据时，这个方法会在收到消息时被调用
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        if (msg == null){
            log.error("Netty-Server------加载客户端报文为空!");
        }
        log.info("Netty-Server------加载客户端报文......【" + ctx.channel().id() + "】" + " :" + msg);
        try {
            if(msg == null){return;}
            String data = (String) msg;
            String channelId = ctx.channel().id().asLongText();
            log.info("channelId="+channelId + "data="+data);
            ctx.writeAndFlush(Unpooled.buffer().writeBytes(("server:"+channelId).getBytes()));
            // netty的编码已经指定，因此可以不需要再次确认编码
            // ctx.writeAndFlush(Unpooled.buffer().writeBytes(channelId.getBytes(CharsetUtil.UTF_8)));
        } catch (Exception e) {
            log.error("channelRead--"+e.toString());
        }
    }

    /**
     * 从客户端收到新的数据、读取完成时调用
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
        log.info("channelReadComplete");
        ctx.flush();
    }

    /**
     * 当出现 Throwable 对象才会被调用，即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException {
        ctx.close();//抛出异常，断开与客户端的连接
        log.info("Netty-Server------"+ctx.channel().id() + " 发生了错误,此连接被关闭" + "此时连通数量: " + BootNettyChannelCache.size());
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelActive(ctx);
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();

        String clientIp = insocket.getAddress().getHostAddress();
        int clientPort = insocket.getPort();
        //获取连接通道唯一标识
        //如果map中不包含此连接，就保存连接
        String channelId = ctx.channel().id().asLongText();
        // 这里我将通道id作为code来使用，实际是需要msg里来摘取的客户端数据里的唯一值的
        // 如果没有则创建 如果有，更新data值
        BootNettyChannel b = BootNettyChannelCache.get(channelId);
        if(b == null){
            BootNettyChannel bootNettyChannel = new BootNettyChannel();
            bootNettyChannel.setChannel(ctx.channel());
            bootNettyChannel.setClientCode(channelId);
            bootNettyChannel.setClientIp(clientIp);
            bootNettyChannel.setClientPort(clientPort);
            bootNettyChannel.setReport_last_data("no data");
            BootNettyChannelCache.save(channelId, bootNettyChannel);
            log.info("Netty-Server------客户端【" + channelId + "】连接netty服务器[IP:" + clientIp + "--->PORT:" + clientPort + "]");
        }
        log.info("Netty-Server-----连接通道数量: " + BootNettyChannelCache.size());
    }

    /**
     * 客户端与服务端 断连时 执行
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelInactive(ctx);
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();

        String clientIp = insocket.getAddress().getHostAddress();

        String channelId = ctx.channel().id().toString();

        BootNettyChannel b = BootNettyChannelCache.get(channelId);
        if(b != null){
            BootNettyChannelCache.remove(channelId);
            log.info("ODF-Socket------客户端【" + channelId + "】退出netty服务器[IP:" + clientIp + "--->PORT:" + insocket.getPort() + "]");
        }
        log.info("Netty-Server-----连接通道数量: " + BootNettyChannelCache.size());
        ctx.close(); //断开连接时，必须关闭，否则造成资源浪费，并发量很大情况下可能造成宕机
    }

    /**
     * 服务端当read超时, 会调用这个方法
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception, IOException {
        super.userEventTriggered(ctx, evt);
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();

        ctx.close();//超时时断开连接
        log.info("userEventTriggered:"+clientIp);
    }
}
